package values

import (
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"time"

	"github.com/shopspring/decimal"
)

// ToFloat32 转换目标为 Float32, 如果转换失败, 则返回0
func ToFloat32(v any) float32 {
	if v == nil {
		return 0
	}
	to, _ := ToFloat32E(v)
	return to
}

// ToFloat32D 转换目标为 Float32, 如果转换失败, 则返回defaultVal指定的默认值
func ToFloat32D(v any, defaultVal float32) float32 {
	if v == nil {
		return defaultVal
	}
	if to, err := ToFloat32E(v); err == nil {
		return to
	}
	return defaultVal
}

// ToFloat32E  转换目标为 Float32, 如果转换失败, 则返回0和错误信息
//   - 注意: 特殊浮点数（如NaN、正无穷、负无穷）无法将其转换为Float32
func ToFloat32E(v any) (float32, error) {
	v = UnderefVal(v)
	if v == nil {
		return 0, errors.New("ToFloat32E: unable to nil to float32")
	}
	switch vs := v.(type) {
	case int:
		return float32(vs), nil
	case int64:
		if float64(vs) <= math.MaxFloat32 {
			return float32(vs), nil
		}
		return 0, fmt.Errorf("ToFloat32E: unable to %v of type int64 to float32 overflow", v)
	case int32:
		return float32(vs), nil
	case int16:
		return float32(vs), nil
	case int8:
		return float32(vs), nil
	case uint:
		if float64(vs) <= math.MaxFloat32 {
			return float32(vs), nil
		}
		return 0, fmt.Errorf("ToFloat32E: unable to %v of type uint to float32 overflow", v)
	case uint64:
		if float64(vs) <= math.MaxFloat32 {
			return float32(vs), nil
		}
		return 0, fmt.Errorf("ToFloat32E: unable to %v of type uint64 to float32 overflow", v)
	case uint32:
		return float32(vs), nil
	case uint16:
		return float32(vs), nil
	case uint8:
		return float32(vs), nil
	case float32:
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float32
		vss := float64(vs)
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type math.NaN to float32", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type math.Inf to float32", v)
		}
		return vs, nil
	case float64:
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float32
		if math.IsNaN(vs) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type math.NaN to float32", v)
		} else if math.IsInf(vs, 0) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type math.Inf to float32", v)
		}
		if vs <= math.MaxFloat32 {
			return float32(vs), nil
		}
		return 0, fmt.Errorf("ToFloat32E: unable to %v of type float64 to float32 overflow", v)
	case string:
		return StringToFloat32E(vs)
	case json.Number:
		return StringToFloat32E(string(vs))
	case time.Weekday:
		return float32(vs), nil
	case time.Month:
		return float32(vs), nil
	case bool:
		if vs {
			return 1, nil
		}
		return 0, nil
	case providerFloat:
		vss := vs.Float()
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float32
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float() interface => math.NaN to float32", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float() interface => math.Inf to float32", v)
		}
		if vss <= math.MaxFloat32 {
			return float32(vss), nil
		}
		return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float() interface to float32 overflow", v)
	case providerFloat32:
		vss := float64(vs.Float32())
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float32
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float32() interface => math.NaN to float32", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float32() interface => math.Inf to float32", v)
		}
		return vs.Float32(), nil
	case providerFloat64:
		vss := vs.Float64()
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float32
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float64() interface => math.NaN to float32", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float64() interface => math.Inf to float32", v)
		}
		if vss <= math.MaxFloat32 {
			return float32(vss), nil
		}
		return 0, fmt.Errorf("ToFloat32E: unable to %v of type Float64() interface to float32 overflow", v)

	default:
		return 0, fmt.Errorf("ToFloat32E: unable to %#v of type %T to float32", v, v)
	}
}

// ToFloat64 转换目标为 Float64, 如果转换失败, 则返回0
func ToFloat64(v any) float64 {
	if v == nil {
		return 0
	}
	to, _ := ToFloat64E(v)
	return to
}

// ToFloat64D 转换目标为 Float64, 如果转换失败, 则返回defaultVal指定的默认值
func ToFloat64D(v any, defaultVal float64) float64 {
	if v == nil {
		return defaultVal
	}
	if to, err := ToFloat64E(v); err == nil {
		return to
	}
	return defaultVal
}

// ToFloat64E  转换目标为 Float64, 如果转换失败, 则返回0和错误信息
func ToFloat64E(v any) (float64, error) {
	v = UnderefVal(v)
	if v == nil {
		return 0, errors.New("ToFloat64E: unable to nil to float64")
	}
	switch vs := v.(type) {
	case int:
		return float64(vs), nil
	case int64:
		if float64(vs) <= math.MaxFloat64 {
			return float64(vs), nil
		}
		return 0, fmt.Errorf("ToFloat64E: unable to %v of type int64 to float64 overflow", v)
	case int32:
		return float64(vs), nil
	case int16:
		return float64(vs), nil
	case int8:
		return float64(vs), nil
	case uint:
		if float64(vs) <= math.MaxFloat64 {
			return float64(vs), nil
		}
		return 0, fmt.Errorf("ToFloat64E: unable to %v of type uint to float64 overflow", v)
	case uint64:
		if float64(vs) <= math.MaxFloat64 {
			return float64(vs), nil
		}
		return 0, fmt.Errorf("ToFloat64E: unable to %v of type uint64 to float64 overflow", v)
	case uint32:
		return float64(vs), nil
	case uint16:
		return float64(vs), nil
	case uint8:
		return float64(vs), nil
	case float32:
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float64
		vss := float64(vs)
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type math.NaN to float64", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type math.Inf to float64", v)
		}
		// 32/64位的浮点数转换后精度丢失，使用shopspring/decimal库处理
		return decimal.NewFromFloat32(vs).InexactFloat64(), nil
	case float64:
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float64
		if math.IsNaN(vs) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type math.NaN to float64", v)
		} else if math.IsInf(vs, 0) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type math.Inf to float64", v)
		}
		return vs, nil
	case string:
		return StringToFloat64E(vs)
	case json.Number:
		return StringToFloat64E(string(vs))
	case time.Weekday:
		return float64(vs), nil
	case time.Month:
		return float64(vs), nil
	case bool:
		if vs {
			return 1, nil
		}
		return 0, nil
	case providerFloat:
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float64
		vss := vs.Float()
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type Float() interface => math.NaN to float64", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type Float() interface => math.Inf to float64", v)
		}
		return vss, nil
	case providerFloat32:
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float64
		vss := float64(vs.Float32())
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type Float32() interface => math.NaN to float64", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type Float32() interface => math.Inf to float64", v)
		}
		// 32/64位的浮点数转换后精度丢失，使用shopspring/decimal库处理
		return decimal.NewFromFloat32(vs.Float32()).InexactFloat64(), nil
	case providerFloat64:
		// 是否特殊浮点数（如NaN、正无穷、负无穷）则无法将其转换为Float64
		vss := vs.Float64()
		if math.IsNaN(vss) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type Float64() interface => math.NaN to float64", v)
		} else if math.IsInf(vss, 0) {
			return 0, fmt.Errorf("ToFloat64E: unable to %v of type Float64() interface => math.Inf to float64", v)
		}
		return vss, nil
	default:
		return 0, fmt.Errorf("ToFloat64E: unable to %#v of type %T to float64", v, v)
	}
}
